﻿//
// Copyright 2008-2011 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

using System;
using System.Diagnostics.Contracts;

namespace YaTools.Yaml
{
    public sealed partial class Dump
    {
        /// <summary>
        ///   Abstract sink of YAML serialization events.
        /// </summary>
        /// <remarks>
        ///   This class is an realization of the Strategy or State pattern.   Derrived classes
        ///   should remain context free.   Add any required context to the Dump class.
        /// </remarks>
        public abstract partial class Presenter
        {
            public virtual void PresentComment( Dump dump, string comment)
            {
                Contract.Requires(dump != null);
                Contract.Requires(comment != null);
            }

            public virtual void WriteSeperation(Dump dump)
            {
                Contract.Requires(dump != null);

                if (dump._pending == Pending.Nothing)
                {
                    return;
                }

                Match(dump, Pending.BreakBefore, string.Empty, writeLine: true);

                Match(dump, Pending.Indent | Pending.Space, string.Empty, writeIndent: true);
                Match(dump, Pending.Indent, string.Empty, writeIndent: true);
                Match(dump, Pending.Space, ' ');
                Match(dump, Pending.SpaceAfter, ' ');
                Match(dump, Pending.BreakAfter, string.Empty, writeLine: true, writeIndent: true);

                if (dump._pending != Pending.Nothing)
                {
                    throw new InvalidOperationException(
                        string.Format("{1} is unable to process Pending.{0}", dump._pending, GetType()));
                }
            }

            protected static void RawWrite(Dump dump, char c)
            {
                Contract.Requires(dump != null);
                Contract.Assume(dump._writer != null);

                dump._writer.Write(c);
            }

            protected static void RawWrite(Dump dump, string s)
            {
                Contract.Requires(dump != null);
                Contract.Requires(s != null);
                Contract.Assume(dump._writer != null);

                dump._writer.Write(s);
            }

            protected static void RawWriteLine(Dump dump)
            {
                Contract.Requires(dump != null);
                Contract.Assume(dump._writer != null);

                dump._writer.WriteLine();                
            }
            
            protected static void RawWriteLine(Dump dump, char c)
            {
                Contract.Requires(dump != null);
                Contract.Assume(dump._writer != null);

                dump._writer.WriteLine(c);
            }

            protected static void RawWriteLine(Dump dump, string s)
            {
                Contract.Requires(dump != null);
                Contract.Requires(s != null);
                Contract.Assume(dump._writer != null);

                dump._writer.WriteLine(s);
            }

            [Obsolete]
            protected static bool Match(Dump dump, Pending bits, Pending setAfter = Pending.Nothing)
            {
                Contract.Requires(dump != null);
                Contract.Requires(bits != Pending.Nothing);
                Contract.Requires(bits > setAfter);

                if ((dump._pending & bits) != bits) return false;
                if (IsMostSignificantSet(dump._pending, bits)) return false;

                dump._pending = (dump._pending & ~bits) | setAfter;
                return true;
            }

            protected static bool Match<T>(
                Dump dump, 
                Pending bits, 
                T output, 
                Pending setAfter = Pending.Nothing, 
                bool writeLine = false,
                bool writeIndent = false )
            {
                Contract.Requires(dump != null);
                Contract.Requires(bits != Pending.Nothing);
                Contract.Requires(bits > setAfter);

                if ((dump._pending & bits) != bits) return false;
                if (IsMostSignificantSet(dump._pending, bits)) return false;

                dump._pending = (dump._pending & ~bits) | setAfter;

                if (writeLine)
                    dump._writer.WriteLine(output);
                else
                    dump._writer.Write(output);

                if (writeIndent)
                    dump._writer.IndentLine(dump.IndentSize());

                return true;
            }

            private static bool IsMostSignificantSet(Pending pending, Pending bits)
            {
                for (ushort msb = 1 << 14; msb != 0; msb >>= 1)
                {
                    if (((ushort) bits & msb) == msb)
                    {
                        return (ushort) pending >= (msb << 1);
                    }
                }
                return true;
            }

            protected static void SetPending(Dump dump, Pending value)
            {
                Contract.Requires(dump != null);
                dump._pending |= value;
            }

            protected static void ClearPending(Dump dump, Pending value)
            {
                Contract.Requires(dump != null);
                dump._pending &= ~value;
            }
        }
    }
}
